﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DistributedGraph.OptFloodSet
{
    /// <summary>
    /// OptFloodSet spezifischer Knoten
    /// Der einzige Unterschied zu FloodSet ist die Methode Broadcast, da die Nachrichtenvermittlung optimiert wurde.
    /// </summary>
    public class Node : DistributedGraph.FloodSet.Node
    {
        #region Typen
        public delegate void MsgEvent(Node sender, Node receiver, Link link, DistributedGraph.FloodSet.Message message);
        public delegate void SimpleEvent(Node node);
        #endregion Typen

        #region Eigenschaften
        /// <summary>
        /// Sende Event. (für Logging)
        /// </summary>
        public event MsgEvent MessageSent;

        /// <summary>
        /// Empfangs Event. (für Logging)
        /// </summary>
        public event MsgEvent MessageReceived;
        #endregion Eigenschaften


        #region Methoden
        /// <summary>
        /// Ergänzt die Empfangsmethode um das Ereignis.
        /// </summary>
        /// <param name="link">Der Link.</param>
        /// <param name="message">Die Nachricht.</param>
        internal override void Receive(Link link, IMessage message)
        {
            base.Receive(link, message);
            if (!FailStops && MessageReceived != null) MessageReceived((Node)(link.SourceNode == this ? link.TargetNode : link.SourceNode), this, link, (DistributedGraph.FloodSet.Message)message);
        }

        /// <summary>
        /// Ergänzt die Sendemethode um das Ereignis.
        /// </summary>
        /// <param name="link">Der Link.</param>
        /// <param name="message">Die Nachricht.</param>
        public override void Send(Link link, IMessage message)
        {
            if (!FailStops && MessageSent != null) MessageSent(this, (Node)(link.SourceNode == this ? link.TargetNode : link.SourceNode), link, (DistributedGraph.FloodSet.Message)message);
            base.Send(link, message);
        }

        /// <summary>
        /// OptFloodSet-spezifische Broadcastmethode.
        /// "Nachrichten" aus der formalen Beschreibung.
        /// Das einzige das verschieden zum FloodSet-Algorithmus ist.
        /// </summary>
        /// <param name="message">Eine Dummynachricht.</param>
        public override void Broadcast(IMessage message)
        {
            if (FailStops)
            {
                // Wenn der Knoten ausgefallen ist, macht er auch nichts...
            }
            else
            {
                if (round == 0) // In der formalen Beschreibung beginnt es bei 1
                {
                    // An alle anderen wird die einelementige (Initial-)Menge W geschickt:
                    base.Broadcast(new Message(valueSetBefore));

                    // Event fürs Senden:
                    if (MessageSent != null) MessageSent(this, this, null, new Message(valueSetBefore));
                    if (MessageReceived != null) MessageReceived(this, this, null, new Message(valueSetBefore));
                }
                else if (round == 1) // In der formalen Beschreibung beginnt es bei 1, jetzt also 2
                {
                    if (valueSetBefore.Count == 1)
                    {
                        // Immer noch nur der Initialwert in der Menge:
                        base.Broadcast(new Message(valueSetBefore));
                    }
                    else
                    {
                        HashSet<int> tempSet = valueSetBefore;
                        tempSet.Remove(Value);
                        HashSet<int> tempSingletonSet = new HashSet<int>();
                        tempSingletonSet.Add(tempSet.First());
                        base.Broadcast(new Message(tempSingletonSet));
                    }

                    // Event fürs Senden:
                    if (MessageSent != null) MessageSent(this, this, null, new Message(valueSetBefore));
                    if (MessageReceived != null) MessageReceived(this, this, null, new Message(valueSetBefore));
                }
                else
                {
                    // After those two messages sent, there is no need for sending another non-null message.
                }

                // Event für das Senden/Empfangen:
                // Er sendet sich selbst KEINE Nachricht
                // Und er sendet nur in den ersten beiden anderen eine Nachricht.
                //if (MessageSent != null) MessageSent(this, this, null, new Message(valueSetBefore));
                //if (MessageReceived != null) MessageReceived(this, this, null, new Message(valueSetBefore));
            }

            roundDone = true;
        }
        #endregion Methoden
    }
}
